// $Id: ST.cs 71 2008-06-24 20:21:42Z nvivo $

using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Antlr.StringTemplate;
using DBLGen.UI.Design.Query.DBAdapter;

namespace DBLGen.Plugins.Export
{
	[Plugin("DBLGen StringTemplate Exporter")]
	[PluginSettings(typeof(STSettings), typeof(STSettingsEditor))]
	public class ST : ExportPlugin
	{
		public new STSettings Settings
		{
			get
			{
				return (STSettings)base.Settings;
			}
			set
			{
				base.Settings = value;
			}
		}

		public override ExportDataCollection Export(Table table)
		{
			ExportDataCollection eic = new ExportDataCollection();

			if (table.CanExport)
			{
				STErrorListener errorListener = new STErrorListener();

				foreach (string templateFile in Settings.Templates)
				{
					string absolutePath = PathUtility.GetAbsolutePath(templateFile);
					string template = File.ReadAllText(absolutePath);

					StringTemplateGroup stg;

					using (TextReader reader = new StringReader(template))
					{
						stg = new StringTemplateGroup(reader, errorListener);
						reader.Close();
					}

					stg.RegisterAttributeRenderer(typeof(string), new STStringRenderer());

					ExportData ed = CreateExportData(stg, table);
					eic.Add(ed);
				}

				if (errorListener.Messages.Count > 0)
				{
					// TODO: ROSH
					// throw new ErrorMessageListException(errorListener.Messages.ToArray());
				}
				
			}

			return eic;
		}

		private static ExportData CreateExportData(StringTemplateGroup stg, Table table)
		{
			StringTemplate fileNameTemplate = stg.GetInstanceOf("FileName");
			StringTemplate contentsTemplate = stg.GetInstanceOf("Contents");

			STTable t = STTable.Create(table, 0);

			fileNameTemplate.SetAttribute("table", t);
			contentsTemplate.SetAttribute("table", t);

			string fileName = fileNameTemplate.ToString();
			string contents = contentsTemplate.ToString();

			ExportData ei = new ExportData(fileName, contents);

			return ei;
		}

		private class STErrorListener : IStringTemplateErrorListener
		{
			public List<ErrorMessage> Messages = new List<ErrorMessage>();

			public void Error(string msg, Exception e)
			{
				Messages.Add(new ErrorMessage(ErrorMessageSeverity.Error, msg));
			}

			public void Warning(string msg)
			{
				Messages.Add(new ErrorMessage(ErrorMessageSeverity.Warning, msg));
			}
		}

		private class STStringRenderer : IAttributeRenderer
		{
			public string ToString(object o, string formatName)
			{
				string[] formatList = formatName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				string value = (string)o;

				foreach (string format in formatList)
				{
					string f = format.Trim().ToLower();

					switch (f)
					{
						case "lower":
							value = value.ToLower();
							break;
						case "upper":
							value = value.ToUpper();
							break;
						case "camel":
							value = TextUtility.ToCamelCase(value);
							break;
						case "pascal":
							value = TextUtility.ToPascalCase(value);
							break;
						default:
							throw new ArgumentException("'" + format + "' is not a valid format.");
					}
				}

				return value;
			}

			public string ToString(object o)
			{
				return (string)o;
			}
		}

		#region Attribute classes

		private class STTable
		{
			public string name;
			public string schema;
			public string fullname;

			public string alias;
			public string comments;
			public string sourcetype;

			public IList<STColumn> columns = new List<STColumn>();
			public IList<STMethod> methods = new List<STMethod>();
			public IList<STColumn> pkcolumns = new List<STColumn>();
			public IList<STColumn> fkcolumns = new List<STColumn>();
			public IList<STColumn> nonpkcolumns = new List<STColumn>();

			private Dictionary<string, string> _items = new Dictionary<string, string>();
			
			public virtual string this[string key]
			{
				get
				{
					string value;

					if (_items.TryGetValue(key, out value))
						return value;
					else
						throw new KeyNotFoundException(String.Format("The item '{0}' does not exist in the table.", key));
				}
			}

			public override string ToString()
			{
				return fullname;
			}
			
			public static STTable Create(Table sourceTable, int tableLevel)
			{
				STTable t = new STTable();

				t.name = sourceTable.Name;
				t.schema = sourceTable.Schema;
				t.fullname = sourceTable.QualifiedName;

				t.alias = sourceTable.Alias;
				t.comments = sourceTable.Comments;
				t.sourcetype = sourceTable.SourceType.ToString();

				foreach (Method sourceMethod in sourceTable.Methods)
				{
					if (sourceMethod.CreateMethod)
					{
						STMethod m = STMethod.Create(sourceMethod, tableLevel);
						t.methods.Add(m);
					}
				}

				foreach (Column sourceColumn in sourceTable.Columns)
				{
					if (sourceColumn.CanExport)
					{
						bool isPk = sourceColumn.IsPrimaryKey;
						bool isFk = sourceColumn.IsForeignKey;

						STColumn c = STColumn.Create(sourceColumn, tableLevel);

						t.columns.Add(c);

						if (isPk)
							t.pkcolumns.Add(c);
						else
							t.nonpkcolumns.Add(c);

						if (isFk)
							t.fkcolumns.Add(c);
					}
				}

				foreach (KeyValuePair<string, string> kvp in sourceTable.CustomAttributes)
					t._items[kvp.Key] = kvp.Value;

				return t;
			}
		}

		private class STMethod
		{
			private string name;
			private string sqlQuery = string.Empty;
			private string comments = string.Empty;
			private bool createMethod = true;
			private IList<STColumn> columns = new List<STColumn>();
			// private CustomAttributes _customAttributes;
			
			public override string ToString()
			{
				return name;
			}
			
			private Dictionary<string, string> _items = new Dictionary<string, string>();

			public static STMethod Create(Method sourceMethod, int tableLevel)
			{
				STMethod m = new STMethod();
				m.name = sourceMethod.Name;
				m.sqlQuery = sourceMethod.SqlQuery;
				m.comments = sourceMethod.Comments;
				m.createMethod = sourceMethod.CreateMethod;
				
				foreach(QueryRow r in sourceMethod.CustomAttributesQuery.GetQueryRows())
				{
					string explorer = r.FieldName;
					for (int i = 0; i < sourceMethod.Table.Columns.Count; ++i)
					{
						if (explorer.Equals(sourceMethod.Table.Columns[i].Name.ToString(), StringComparison.InvariantCultureIgnoreCase))
						{
							STColumn s = STColumn.Create(sourceMethod.Table.Columns[i], 0);
							m.columns.Add(s);
						}
					}
				}

				//foreach (KeyValuePair<string, string> kvp in sourceMethod.CustomAttributesQuery)
				//  m._items[kvp.Key] = kvp.Value;
				return m;
			}

		}

		private class STColumn
		{
			#region 
			public string name;
			public string sqltype;
			public int? length;
			public int? octetlength;
			public int? precision;
			public int? scale;
			public bool isnullable;
			public bool iscomputed;
			public bool ispk;
			public bool isfk;

			public string alias;
			private string _type_;
			public string comments;
			public bool isreadonly;
			#endregion

			public STTable fktable;
			public STColumn fkcolumn;

			private Dictionary<string, string> _items = new Dictionary<string, string>();

			public virtual string this[string key]
			{
				get
				{
					string value;

					if (_items.TryGetValue(key, out value))
						return value;
					else
						throw new KeyNotFoundException(String.Format("The attribute '{0}' does not exist in the column.", key));
				}
			}

			public string type
			{
				get
				{
					return _type_;
				}
			}

			public override string ToString()
			{
				return name;
			}

			public static STColumn Create(Column sourceColumn, int tableLevel)
			{
				STColumn c = new STColumn();

				c.name = sourceColumn.Name;
				c.sqltype = sourceColumn.SqlType;
				c.length = sourceColumn.Length;
				c.octetlength = sourceColumn.OctetLength;
				c.precision = sourceColumn.Precision;
				c.scale = sourceColumn.Scale;
				c.isnullable = sourceColumn.IsNullable;
				c.iscomputed = sourceColumn.IsComputed;
				c.ispk = sourceColumn.IsPrimaryKey;
				c.isfk = sourceColumn.IsForeignKey;

				c.alias = sourceColumn.Alias;
				c._type_ = sourceColumn.Type;
				c.comments = sourceColumn.Comments;
				c.isreadonly = sourceColumn.IsReadOnly;

				if (sourceColumn.IsForeignKey && tableLevel == 0)
				{
					c.fktable = STTable.Create(sourceColumn.GetReferencedTable(), tableLevel + 1);
					c.fkcolumn = STColumn.Create(sourceColumn.GetReferencedColumn(), tableLevel + 1);
				}

				foreach (KeyValuePair<string, string> kvp in sourceColumn.CustomAttributes)
					c._items[kvp.Key] = kvp.Value;

				return c;
			}
		}

		#endregion
	}
}
